package cn.dt.ymcc.service.impl;

import cn.dt.ymcc.GlobalException;
import cn.dt.ymcc.domain.AlipayInfo;
import cn.dt.ymcc.domain.PayOrder;
import cn.dt.ymcc.dto.AlipayNotifyDto;
import cn.dt.ymcc.dto.PayApplyDto;
import cn.dt.ymcc.dto.PayNotifyDto;
import cn.dt.ymcc.mapper.PayOrderMapper;
import cn.dt.ymcc.service.IAlipayInfoService;
import cn.dt.ymcc.service.IPayOrderService;
import cn.dt.ymcc.util.AssertUtil;
import com.alibaba.fastjson.JSON;
import com.alipay.easysdk.factory.Factory;
import com.alipay.easysdk.kernel.Config;
import com.alipay.easysdk.kernel.util.ResponseChecker;
import com.alipay.easysdk.payment.common.models.AlipayTradeRefundResponse;
import com.alipay.easysdk.payment.page.Client;
import com.alipay.easysdk.payment.page.models.AlipayTradePagePayResponse;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.baomidou.mybatisplus.mapper.Wrapper;
import com.baomidou.mybatisplus.service.impl.ServiceImpl;
import org.apache.rocketmq.client.producer.LocalTransactionState;
import org.apache.rocketmq.client.producer.SendStatus;
import org.apache.rocketmq.client.producer.TransactionSendResult;
import org.apache.rocketmq.spring.core.RocketMQListener;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.messaging.Message;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.Map;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author whale.dai
 * @since 2022-07-30
 */
@Service
public class PayOrderServiceImpl extends ServiceImpl<PayOrderMapper, PayOrder> implements IPayOrderService {
    @Autowired
    private IPayOrderService payOrderService;
    @Autowired
    private IAlipayInfoService alipayInfoService;
    @Autowired
    private RocketMQTemplate rocketMQTemplate;
    @Autowired
    private RedisTemplate<Object,Object>redisTemplate;

    @Override
    public PayOrder selectByOrderNo(String orderNo) {
        Wrapper<PayOrder> wrapper = new EntityWrapper<>();
        wrapper.eq("order_no",orderNo);
        PayOrder payOrder = selectOne(wrapper);
        return payOrder;
    }

    /**
     * 1.通过订单号查询支付单是否存在
     * 还应该判断支付类型再选择对应的支付方式
     * 2.订单号状态是否为待支付
     * 3.发起支付请求
     * @param dto
     * @return
     */
    @Override
    public String apply(PayApplyDto dto) {
        PayOrder payOrder = payOrderService.selectByOrderNo(dto.getOrderNo());
        AssertUtil.isNotNull(payOrder,"支付单不存在");
        if(payOrder.getPayStatus()==PayOrder.STATUS_PAY_WAIT){
            return aliApply(dto,payOrder);
        }
        return null;

    }



    private String aliApply(PayApplyDto dto, PayOrder payOrder) {
        AlipayInfo info = alipayInfoService.selectOne(null);
        // 1. 设置参数（全局只需设置一次）
        Factory.setOptions(getOptions(info));
        try {
            // 2. 网页端支付
            Client page = Factory.Payment.Page();
            //封装支付相关参数，发起支付
            AlipayTradePagePayResponse response = page.pay(
                    payOrder.getSubject(),
                    payOrder.getOrderNo(),
                    payOrder.getAmount().toString(),
                    StringUtils.isEmpty(dto.getCallUrl())?info.getReturnUrl():dto.getCallUrl()//同步回调地址
            );
            // 3. 处理响应或异常
            if (ResponseChecker.success(response)) {
                System.out.println("调用成功");
                return response.getBody();//Form表单
            } else {
                System.err.println("调用失败，原因：");
                throw new GlobalException("支付调用失败！！！");
            }
        } catch (Exception e) {
            System.err.println("调用遭遇异常，原因：" + e.getMessage());
            throw new RuntimeException(e.getMessage(), e);
        }
    }

    public Config getOptions(AlipayInfo info) {
        Config config = new Config();
        config.protocol = info.getProtocol();
        config.gatewayHost = info.getGatewayHost();//沙箱API地址
        config.signType = info.getSignType();
        config.appId = info.getAppId();
        // 为避免私钥随源码泄露，推荐从文件中读取私钥字符串而不是写入源码中
        config.merchantPrivateKey = info.getMerchantPrivateKey();
        //注：如果采用非证书模式，则无需赋值上面的三个证书路径，改为赋值如下的支付宝公钥字符串即可
        config.alipayPublicKey = info.getAlipayPublicKey();
        //可设置异步通知接收服务地址（可选）
        config.notifyUrl = info.getNotifyUrl();
        //可设置AES密钥，调用AES加解密相关接口时需要（可选）
        config.encryptKey = "";
        return config;
    }

    /**
     * 发送mq事务消息
     * 从支付宝拿到数据，进行校验
     * 校验通过订单查询支付单是否存在，查询支付宝支付单是否是支付完成，支付状态是否待支付
     * 支付成功后的异步回调，使用rocketMq将支付订单存入org然后在listener里面修改支付订单
     * 将courselearning要存的数据和订单状态存入message,课程id存入（）
     * @param dto
     * @return
     */
    @Override
    public String successCallback(AlipayNotifyDto dto) throws Exception {
        String dtoStr = JSON.toJSONString(dto);
        Map<String,String> dtoMap = JSON.parseObject(dtoStr, Map.class);
        PayOrder payOrder = selectByOrderNo(dto.getOut_trade_no());
        //判断支付宝返回是否成功了！不成功就等支付宝最大可能响应
        checkParameters(dto, dtoMap, payOrder);
        //退款逻辑方法(如果超时之后再支付成功查看支付单支付状态是否是取消如果是就退款)
        refund(payOrder);
        PayNotifyDto notifyDto = new PayNotifyDto(dto.getOut_trade_no(),payOrder.getExtParams());
        //发起事务消息
        Message<String> message = MessageBuilder.withPayload(JSON.toJSONString(notifyDto)).build();
        TransactionSendResult result = rocketMQTemplate.sendMessageInTransaction(
                "service-pay",//要和监听器上注解申明的名字一致
                "pay-notify-topic:pay-notify-tag",
                message,  //消息体
                payOrder);//额外参数)
        LocalTransactionState localTransactionState = result.getLocalTransactionState();
        SendStatus sendStatus = result.getSendStatus();
        if(localTransactionState!=LocalTransactionState.COMMIT_MESSAGE||sendStatus!=SendStatus.SEND_OK){
            throw new GlobalException("处理支付成功结果失败！！");
        }
        //支付成功删除预存订单
        redisTemplate.delete(dto.getOut_trade_no());
        return "success";

    }

    /**
     * 如果超时之后再支付成功查看支付单支付状态是否是取消如果是就退款,调用支付宝进行退款
     * @param payOrder
     */
    private String refund(PayOrder payOrder) {
        try {
            if(payOrder.getPayStatus()==4){
                Factory.Payment.Common().refund(payOrder.getOrderNo(), payOrder.getAmount().toString());

            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "success";
    }

    /**
     * 校验参数是否合法
     * @param dto
     * @param dtoMap
     * @param payOrder
     * @return
     * @throws Exception
     */
    private static String checkParameters(AlipayNotifyDto dto, Map<String, String> dtoMap, PayOrder payOrder) throws Exception {
        Boolean aBoolean = Factory.Payment.Common().verifyNotify(dtoMap);
        if (!aBoolean
                || payOrder == null
                || !dto.getTrade_status().equals(AlipayNotifyDto.TRADE_SUCCESS)
                || payOrder.getPayStatus() != PayOrder.STATUS_PAY_WAIT
        ){
            return "false";
        }
         return null ;
    }
}
